/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package proy1sistdistribuidos.modelo;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Observer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.SecretKey;
import javax.swing.JOptionPane;
import proy1sistdistribuidos.control.advertisements.AdvertisementContenidoFisicoImpl;
import proy1sistdistribuidos.control.advertisements.AdvertisementImagenImpl;
import proy1sistdistribuidos.interfaz.BuscarContenidoInicio;
import proy1sistdistribuidos.interfaz.BuscarSimilitudInicio;
import proy1sistdistribuidos.modelo.seguridad.EncriptadorDesencriptador;
import proy1sistdistribuidos.modelo.seguridad.EncriptadorDesencriptadorAES;
import sun.misc.BASE64Decoder;

/**
 * @author Carlos Diaz Maya, Juan Felipe Garcia
 */
public class ControladorProySistDistribuidos {
    private AdministradorJXTA administrador;
    private String idUsuario;
    public static final String rutaCarpetaDescarga = "./RepositorioImagenes/";
    private ControladorExclusion contExclusion;
    private ServidorSocketEstados servSocketEstados;

    public ControladorProySistDistribuidos(String idUsuario) throws Exception {
        contExclusion = new ControladorExclusion();
        administrador = new AdministradorJXTA(idUsuario);
        administrador.addObserver(contExclusion);
        administrador.publicarNodo();
        //Inicia el servidor de sockets
        servSocketEstados = new ServidorSocketEstados();
        Thread hiloPrincipalServSockets = new Thread(servSocketEstados);
        hiloPrincipalServSockets.start();

        File carp = new File(rutaCarpetaDescarga);
        if(!carp.exists())
            carp.mkdirs();
        FileFilter filtro = new FileFilter() {
            public boolean accept(File ruta) {
                boolean aceptado = false;
                String nomArch = ruta.getName();
                String[] partesNom = nomArch.split("\\.");

                if(partesNom.length > 1){
                    String extension = partesNom[partesNom.length-1];
                    if(extension.equalsIgnoreCase("png") ||
                       extension.equalsIgnoreCase("gif") ||
                       extension.equalsIgnoreCase("jpg") ||
                       extension.equalsIgnoreCase("jpeg")) {
                        return true;
                    } else return false;
                } else return false;
            }
        };

        //Publica todos los archivos ofrecidos por el usuario
        File[] archivos = carp.listFiles(filtro);

        //Se invoca el desencriptador de la imagen con la llave AES del equipo local
        EncriptadorDesencriptador encdesclocal = EncriptadorDesencriptadorAES.obtenerSolitario();
        SecretKey llaveLocal = AdministradorJXTA.obtenerLlaveAES();
        if(llaveLocal==null){
            //-- En caso de no tener una llave, se crea y se guarda en disco
             llaveLocal = encdesclocal.construirLlave();
            AdministradorJXTA.guardarLlaveAES(llaveLocal);
        }

        for(File archivo:archivos){
            String nomArch = archivo.getName();
            String[] partesNom = nomArch.split("\\."); 
            File archivoInfoPersonas = new File(rutaCarpetaDescarga+partesNom[0]+".ips");
            List<String[]> personas = new LinkedList<String[]>();
            if(!archivoInfoPersonas.exists()){
                personas = null;
            } else {
                FileReader fil = new FileReader(archivoInfoPersonas);
                BufferedReader lector = new BufferedReader(fil);
                String linea = null;
                while((linea=lector.readLine())!=null){
                    String[] info = linea.split(";");
                    if(info.length>=2){
                        String[] result = new String[2];
                        result[0] = encdesclocal.desencriptarMensaje(info[0], llaveLocal);
                        result[1] = encdesclocal.desencriptarMensaje(info[1], llaveLocal);
                        personas.add(result);
                    }
                }
                lector.close();
                fil.close();
            }

            //TODO: TRAER EL SENSIBLE!!!!!!!!!!
            boolean sensible = false;

            File archTemp = File.createTempFile(partesNom[0], "."+partesNom[1]);
            String carpetaUbicacionArchTemp = archTemp.getPath().substring(0, archTemp.getPath().length()-archTemp.getName().length());
            File nuevoNombreArch = new File(carpetaUbicacionArchTemp + partesNom[0] + "." + partesNom[1]);
            if(nuevoNombreArch.exists()){
                nuevoNombreArch.delete();
            }
            boolean renameTo = archTemp.renameTo(nuevoNombreArch);


            FileReader readerOrig = new FileReader(archivo);
            BufferedReader buf = new BufferedReader(readerOrig);

            String linea="";
            String todaLaCadena="";
            while((linea = buf.readLine()) != null){
                todaLaCadena+=linea;
            }

            String imgBase64 = encdesclocal.desencriptarMensaje(todaLaCadena, llaveLocal);

            BASE64Decoder dec = new BASE64Decoder();
            byte[] imagen = dec.decodeBuffer(imgBase64);

            //Se escribe la imagen
            FileOutputStream fos = new FileOutputStream(nuevoNombreArch);
            fos.write(imagen);
            fos.close();


            
            administrador.publicarImagen(partesNom[0], personas, nuevoNombreArch, sensible);

            archTemp.delete();
            nuevoNombreArch.delete();
        }

        //inicia recuperacion estado
        File arranque = new File(ControladorEstado.RUTAFISICA);
        if(arranque.exists()){
            FileReader reader = new FileReader(arranque);
            BufferedReader buffer = new BufferedReader(reader);
            StringBuffer sbuffer = new StringBuffer();
            String linea = "";

            while((linea = buffer.readLine())!=null ){
                sbuffer.append(linea);
            }
            buffer.close();
            reader.close();

            EstructuraDatosEstado estructuraEstado = new EstructuraDatosEstado(sbuffer.toString());
            EstadoGeneral estadoGeneral = estructuraEstado.getEstadoGeneralInicial();
            ControladorEstado.obtenerInstancia().setEstadoGlobalDescarga(estructuraEstado.getEstadoGlobalDescarga());
            ControladorEstado.obtenerInstancia().setEstadoLocalDescarga(estadoGeneral);
            ControladorEstado.obtenerInstancia().setListaNodos(estadoGeneral.getListaNodos());

            if(!(estadoGeneral.getNombreEstado().equals(EstadoGeneral.LIBRERAR_PERMISO_OTORGADO)) &&
                    !(estadoGeneral.getNombreEstado().equals(EstadoGeneral.ARCHIVO_DESCARGADO)) &&
                    !(estadoGeneral.getNombreEstado().equals(EstadoGeneral.INACTIVO)) &&
                    !(estadoGeneral.getNombreEstado().equals(EstadoGeneral.PROCESAR_DESCARGA)) &&
                    !(estadoGeneral.getNombreEstado().equals(EstadoGeneral.ENVIAR_RESPUESTA_SOLICITUD_PERMISO)) &&
                    !(estadoGeneral.getNombreEstado().equals(EstadoGeneral.SIN_PERMISO_CONSULTA))){
                
                BuscarSimilitudInicio simi = new BuscarSimilitudInicio(this);
                this.addObserver(simi);
                simi.setVisible(true);
            }

            if(estadoGeneral.getNombreEstado().equals(EstadoGeneral.SOLICITAR_PERMISO)){
                ArrayList<NodoRemoto> estado = estadoGeneral.getListaNodos();
                //settear correctamente los observadores y los observados para invocar: notifyObserver (estado)
                administrador.notificarObservadoresCambioEstado(estado);
                
            } else if(estadoGeneral.getNombreEstado().equals(EstadoGeneral.RECIBIR_PERMISO)){
                //settear correctamente los observadores y los observados para notificar permiso
                contExclusion.getCont().notificarPermiso(Boolean.parseBoolean(estadoGeneral.getMensaje()));
            } else if(estadoGeneral.getNombreEstado().equals(EstadoGeneral.SIN_PERMISO_CONSULTA)){
                // no se recupera en este metodo
            } else if(estadoGeneral.getNombreEstado().equals(EstadoGeneral.INVOCAR_CONSULTA)){
                administrador.buscarFotoMasParecidaAImagen(estadoGeneral.getImagenEntrada());

            } else if(estadoGeneral.getNombreEstado().equals(EstadoGeneral.CONSULTA_PROCESADA)){
                  List<AdvertisementImagenImpl> lista = estadoGeneral.getListaResultadosConsulta();
                //settear correctamente los observadores y los observados 
                administrador.notificarObservadoresCambioEstado(lista);
            } else if(estadoGeneral.getNombreEstado().equals(EstadoGeneral.INVOCAR_DESCARGA)){

                administrador.obtenerInformacionImagen(estadoGeneral.getEtiquetaImagen());

            } else if(estadoGeneral.getNombreEstado().equals(EstadoGeneral.PROCESAR_DESCARGA)){
                //settear correctamente los observadores y los observados
                //administrador.notificarObservadoresCambioEstado(estadoGeneral.getImagenProcesada());


                AdvertisementContenidoFisicoImpl cont = (AdvertisementContenidoFisicoImpl) estadoGeneral.getImagenProcesada();
                String nomarch = estadoGeneral.getAdvertisementImagen().getEtiqueta();

                if (nomarch != null) {
                    try {
                        AdvertisementImagenImpl infoImg = estadoGeneral.getAdvertisementImagen();

                        String extension = "";
                        String mime = cont.getMimeTypeArchivoFisico();
                        if (mime.equals(TiposMime.MIMETYPEPNG)) {
                            extension = ".png";
                        } else if (mime.equals(TiposMime.MIMETYPEGIF)) {
                            extension = ".gif";
                        } else if (mime.equals(TiposMime.MIMETYPEJPG)) {
                            extension = ".jpg";
                        }

                        String nombreArchivoImagen = ControladorProySistDistribuidos.rutaCarpetaDescarga + nomarch + extension;

                        File archivo = new File(nombreArchivoImagen);
                        if (!archivo.exists()) {
                            archivo.createNewFile();
                        }

                        //Se escribe la imagen
                        FileOutputStream fos = new FileOutputStream(archivo);

                        String contenidoArchivoB64 = cont.getContenidoBase64();
                        BASE64Decoder dec = new BASE64Decoder();
                        byte[] imagen = dec.decodeBuffer(contenidoArchivoB64);

                        fos.write(imagen);
                        fos.close();


                        File archivoPersonas = new File(ControladorProySistDistribuidos.rutaCarpetaDescarga + nomarch + ".ips");
                        if (!archivoPersonas.exists()) {
                            archivoPersonas.createNewFile();
                        }

                        PrintWriter wrt1 = new PrintWriter(archivoPersonas);

                        List<String[]> pers = infoImg.getListaPersonas();
                        if (pers == null) {
                            wrt1.close();
                        } else {
                            for (String[] per : pers) {
                                wrt1.println(per[0] + ";" + per[1]);
                            }
                            wrt1.close();
                        }

                        File archivoDetalles = new File(ControladorProySistDistribuidos.rutaCarpetaDescarga + nomarch + ".iim");
                        if (!archivoDetalles.exists()) {
                            archivoDetalles.createNewFile();
                        }

                        PrintWriter wrt2 = new PrintWriter(archivoDetalles);
                        wrt2.println(infoImg.getEtiqueta());
                        wrt2.println(infoImg.getFechaCreacion());
                        wrt2.println(AdvertisementImagenImpl.formato.format(new Date()));

                        wrt2.close();
                        JOptionPane.showMessageDialog(null, "Archivo descargado exitosamente.", "Exito", JOptionPane.INFORMATION_MESSAGE);

                    } catch (Exception ex) {
                        Logger.getLogger(ControladorProySistDistribuidos.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }

            
            } else if(estadoGeneral.getNombreEstado().equals(EstadoGeneral.ARCHIVO_DESCARGADO)){
                 JOptionPane.showMessageDialog(null, "Archivo descargado exitosamente.", "Exito", JOptionPane.INFORMATION_MESSAGE);

            } else if(estadoGeneral.getNombreEstado().equals(EstadoGeneral.LIBRERAR_PERMISO_OTORGADO)){
                 ControladorEstado.obtenerInstancia().setEstadoLocal(ControladorEstado.ESTADOABIERTO);

            }

            if((estadoGeneral.getNombreEstado().equals(EstadoGeneral.LIBRERAR_PERMISO_OTORGADO)) ||
                    (estadoGeneral.getNombreEstado().equals(EstadoGeneral.ARCHIVO_DESCARGADO)) ||
                    (estadoGeneral.getNombreEstado().equals(EstadoGeneral.SIN_PERMISO_CONSULTA)) ||
                    (estadoGeneral.getNombreEstado().equals(EstadoGeneral.ENVIAR_RESPUESTA_SOLICITUD_PERMISO)) ||
                    (estadoGeneral.getNombreEstado().equals(EstadoGeneral.PROCESAR_DESCARGA))){
                
                 ControladorEstado.obtenerInstancia().setEstadoLocal(ControladorEstado.ESTADOABIERTO);
                 String laIp = estadoGeneral.getIdentificadorFuente();
                estadoGeneral = new EstadoGeneral();
                estadoGeneral.setNombreEstado(estadoGeneral.INACTIVO);
                estadoGeneral.setIdentificadorFuente(laIp);
                ControladorEstado.obtenerInstancia().actualizarEstadoGeneralInicial(estadoGeneral);

            }
            
        }

    }

    public void buscarArchivosPorEtiqueta(String etiqueta) {
       administrador.buscarArchivosPorEtiqueta(etiqueta);
    }

    public void buscarArchivosPorPersonas(List<String[]> personas){
        administrador.buscarImagenesPorNombresPersonas(personas);
    }
    
    public void buscarFotoMasParecidaAImagen(byte[] imagenDada) throws IOException{
        administrador.buscarFotoMasParecidaAImagen(imagenDada);
    }

    public void obtenerInformacionImagen(String idEtiqueta) {
        administrador.obtenerInformacionImagen(idEtiqueta);
    }

    public void publicarImagen(String etiqueta, List<String[]> personas, File archivo, boolean sensible) throws Exception {
        administrador.publicarImagen(etiqueta, personas, archivo, sensible);
    }

    public File[] obtenerArchivosImagenDescargados() {
      File carp = new File(rutaCarpetaDescarga);
        if(!carp.exists())
            carp.mkdirs();
        FileFilter filtro = new FileFilter() {
            public boolean accept(File ruta) {
                boolean aceptado = false;
                String nomArch = ruta.getName();
                String[] partesNom = nomArch.split("\\.");

                if(partesNom.length > 1){
                    String extension = partesNom[partesNom.length-1];
                    if(extension.equalsIgnoreCase("png") ||
                       extension.equalsIgnoreCase("gif") ||
                       extension.equalsIgnoreCase("jpg") ||
                       extension.equalsIgnoreCase("jpeg")) {
                        return true;
                    } else return false;
                } else return false;
            }
        };

         //Publica todos los archivos ofrecidos por el usuario
        File[] archivos = carp.listFiles(filtro);

        return archivos;
    }

    public List<String[]> obtenerInformacionArchivoInfPersonas(File archivo) throws Exception {
        List<String[]> personas = null;
        String nomArch = archivo.getName();
        String[] partesNom = nomArch.split("\\.");
        File archivoInfoPersonas = new File(rutaCarpetaDescarga+partesNom[0]+".ips");
        if(!archivoInfoPersonas.exists()){
            personas = null;
        } else {
            personas = new ArrayList<String[]>();
            FileReader fil = new FileReader(archivoInfoPersonas);
            BufferedReader lector = new BufferedReader(fil);
            String linea = null;

            //Se invoca el desencriptador de la imagen con la llave AES del equipo local
            EncriptadorDesencriptador encdesclocal = EncriptadorDesencriptadorAES.obtenerSolitario();
            SecretKey llaveLocal = AdministradorJXTA.obtenerLlaveAES();
            if(llaveLocal==null){
                //-- En caso de no tener una llave, se crea y se guarda en disco
                 llaveLocal = encdesclocal.construirLlave();
                AdministradorJXTA.guardarLlaveAES(llaveLocal);
            }

            while((linea=lector.readLine())!=null){
                String lineaDesenc = encdesclocal.desencriptarMensaje(linea, llaveLocal);
                String[] info = lineaDesenc.split(";");
                if(info.length>=2){
                    String[] result = new String[2];
                    result[0] = info[0];
                    result[1] = info[1];
                    personas.add(result);
                }
            }
            lector.close();
            fil.close();
        }
        return personas;
    }

    public List<String> obtenerInformacionArchivoInfDescarga(File archivo) throws Exception {
        List<String> infoArchivo = null;
        String nomArch = archivo.getName();
        String[] partesNom = nomArch.split("\\.");
        File archivoInfoDescarga = new File(rutaCarpetaDescarga+partesNom[0]+".iim");

        //Se invoca el desencriptador de la imagen con la llave AES del equipo local
        EncriptadorDesencriptador encdesclocal = EncriptadorDesencriptadorAES.obtenerSolitario();
        SecretKey llaveLocal = AdministradorJXTA.obtenerLlaveAES();
        if(llaveLocal==null){
            //-- En caso de no tener una llave, se crea y se guarda en disco
             llaveLocal = encdesclocal.construirLlave();
            AdministradorJXTA.guardarLlaveAES(llaveLocal);
        }

        if(!archivoInfoDescarga.exists()){
            infoArchivo = null;
        } else {
            infoArchivo = new ArrayList<String>();
            FileReader fil = new FileReader(archivoInfoDescarga);
            BufferedReader lector = new BufferedReader(fil);
            String linea = null;
            while((linea=lector.readLine())!=null){
               String lineaDesenc = encdesclocal.desencriptarMensaje(linea, llaveLocal);
               infoArchivo.add(lineaDesenc);
            }
            lector.close();
            fil.close();
        }
        return infoArchivo;
    }

    
     public void addObserver(Observer ob){
        administrador.addObserver(ob);
        contExclusion.adicionarObservador(ob);
        administrador.addObserver(contExclusion);
    }

    public String calcularHashEtiqueta(String idEtiqueta) throws NoSuchAlgorithmException {
       //----Inicio codigo creacion hash ----
            //Adaptado del codigo disponible en:
            //http://www.mkyong.com/java/java-sha-hashing-example/
            //para su uso en este proyecto.

            //Se crea un indice SHA-256 con base a la etiqueta
            //
            MessageDigest md = MessageDigest.getInstance("SHA-256");

            byte[] etiquetaEnBytes = idEtiqueta.getBytes();
            //Se pasa el arreglo al "digester" para que la pase a SHA-256
            md.update(etiquetaEnBytes);

            //Se guarda el resultado en otro arreglo de bytes
            byte[] arrbtDigest = md.digest();


            //Lineas tomadas directamente del ejemplo disponible en
            //http://www.mkyong.com/java/java-sha-hashing-example/
            //donde se pasa el hash a formato hexadecimal
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < arrbtDigest.length; i++) {
                sb.append(Integer.toString((arrbtDigest[i] & 0xff) + 0x100, 16).substring(1));
            }

            String indiceEtiqueta = sb.toString();
            //--- Fin código creación hash
            return indiceEtiqueta;
    }

    public void removeObserver(Observer aThis) {
        administrador.deleteObserver(aThis);
        contExclusion.eliminarObservador(aThis);
    }

    public void solicitarPermisoNodos() {
        //Se invoca la busqueda. Despues, llega el controlador de exclusion mutua y recibe los resultados de la misma
        //y procesa la solicitud del permiso
        administrador.buscarInformacionNodos();
    }

}
